﻿#region Software License
/**************************************************************/
/*          Business of Software Consulting                   */
/*          FREE SOFTWARE LICENSE                             */
/**************************************************************/
/*  Directory Manifest Generator                              */
/*  by: Mike Cating                                           */
/*  © 2009 Business of Software Consulting                    */
/*                                                            */
/* BUSINESS OF SOFTWARE CONSULTING DISCLAIMS ALL WARRANTIES,  */
/* RESPONSIBILITIES, AND LIABILITIES ASSOCIATED WITH          */
/* USE OF THIS CODE IN ANY WAY, SHAPE, OR FORM                */
/* REGARDLESS HOW IMPLICIT, EXPLICIT, OR OBSCURE IT           */
/* IS. IF THERE IS ANYTHING QUESTIONABLE WITH REGARDS         */
/* TO THIS SOFTWARE BREAKING AND YOU GAIN A LOSS OF           */
/* ANY NATURE, WE ARE NOT THE RESPONSIBLE PARTY. USE          */
/* OF THIS SOFTWARE CREATES ACCEPTANCE OF THESE TERMS         */
/*                                                            */
/* USE OF THIS CODE MUST RETAIN ALL COPYRIGHT NOTICES         */
/* AND LICENSES (MEANING THIS TEXT).                          */
/*                                                            */
/**************************************************************/
#endregion


using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Globalization;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics;
using System.Security.Permissions;
using NullFX.Security;

namespace BOSTools.DirectoryManifestGenerator
{

    public class FileDetailReader
    {
        public event EventHandler<DirectoryEventArgs> DirectoryProcessed;

        private Queue<DirectorySpec> _pendingDirectories = new Queue<DirectorySpec>();
        private string _sourceDir;
        private Crc32 _crc32 = new Crc32();

        public FileDetailReader(string sourceDir)
        {
            if (string.IsNullOrEmpty(sourceDir))
            {
                throw new ArgumentException("sourceDir was empty or null");
            }

            if (!Directory.Exists(sourceDir))
            {
                throw new ArgumentException(
                   string.Format( CultureInfo.InvariantCulture, "Directory {0} not found or access denied", sourceDir));
            }

            _sourceDir = sourceDir;

            AddDirectoryToQueue(sourceDir);
        }

        [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
        [FileIOPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
        public FileDetail GetNextFile()
        {
            if (_pendingDirectories.Count == 0)
            {
                return null;
            }

            string nextFilePath = null;
            while (_pendingDirectories.Count > 0 && nextFilePath == null )
            {
                DirectorySpec dir = _pendingDirectories.Peek();
                nextFilePath = dir.NextFilePath;

                if (nextFilePath == null)
                {
                    FireDirectoryProcessed( new DirectoryEventArgs( GetRelativePathFromAbsolutePath(dir.DirectoryPath) ) );

                    _pendingDirectories.Dequeue();
                    DirectoryInfo dirDetail = new DirectoryInfo(dir.DirectoryPath);
                    foreach (DirectoryInfo subDir in dirDetail.GetDirectories())
                    {
                        AddDirectoryToQueue(subDir.FullName);
                    }
                }
            }

            if (null != nextFilePath)
            {
                return GetFileDetailForFile(nextFilePath);
            }
            else
            {
                return null;
            }
        }

        private void FireDirectoryProcessed(DirectoryEventArgs e)
        {
            EventHandler<DirectoryEventArgs> temp = DirectoryProcessed;
            if (temp != null)
                temp(this, e);

        }

        private void AddDirectoryToQueue(string dir)
        {
            _pendingDirectories.Enqueue( FileDetailReader.GetSpecForDirectory( dir ));
        }

        private static DirectorySpec GetSpecForDirectory(string dir)
        {
            DirectoryInfo info = new DirectoryInfo( dir );
            FileInfo [] files = info.GetFiles();

            DirectorySpec spec = new DirectorySpec(dir, files);
            return spec;
        }

        [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase"), FileIOPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
        public FileDetail GetFileDetailForFile(string fileName)
        {
            if (string.IsNullOrEmpty(fileName))
            {
                throw new ArgumentException("fileName was null or empty");
            }

            if (!File.Exists(fileName))
            {
                return null;
            }

            FileInfo info = new FileInfo(fileName);
            FileDetail detail = new FileDetail();
            detail.RelativeFileName = GetRelativePathFromAbsolutePath(info.FullName);
            detail.FileLengthBytes = info.Length;
            detail.DateCreated = info.CreationTime;
            detail.DateModified = info.LastWriteTime;

            FileVersionInfo fileVersion = FileVersionInfo.GetVersionInfo(fileName);
            if (!string.IsNullOrEmpty(fileVersion.FileVersion))
            {
                detail.FileVersion = new VersionInfo(fileVersion.FileMajorPart, fileVersion.FileMinorPart, fileVersion.FileBuildPart, fileVersion.FilePrivatePart);
            }

            if (!string.IsNullOrEmpty(fileVersion.ProductVersion))
            {
                detail.ProductVersion = new VersionInfo(fileVersion.ProductMajorPart, fileVersion.ProductMinorPart, fileVersion.ProductBuildPart, fileVersion.ProductPrivatePart);
            }

            using ( FileStream filestream = new FileStream( info.FullName, FileMode.Open, FileAccess.Read ))
            {
                detail.Checksum = _crc32.ComputeChecksum(filestream).ToString("X", CultureInfo.InvariantCulture);
            }

            return detail;
        }

        [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")]
        public string GetRelativePathFromAbsolutePath(string absolutePath)
        {
            string relPath = absolutePath.ToLowerInvariant().Replace(
                _sourceDir.ToLowerInvariant(), "");

            if (relPath.Length > 1 && relPath[0] == '\\')
            {
                relPath = relPath.Substring(1);
            }

            return relPath;
        }
    }
}
